
#include <ov5640_camera.h>
#include <ov5640_port.h>
#include <ov5640_bsp_msp.h>
#include <ov5640_xclk.h>

/**
 * @brief TIM MSP Initialization
 *        This function configures the hardware resources used in this example:
 *           - Peripheral's clock enable
 *           - Peripheral's GPIO Configuration
 * @param htim: TIM handle pointer
 * @retval None
 */
void ov5640_xclk_msp_init()
{
    GPIO_InitTypeDef GPIO_InitStruct;
    /*##-1- Enable peripherals and GPIO Clocks #################################*/
    /* TIMx Peripheral clock enable */
    OV5640XCLK_CLK_ENABLE();

    /* Enable all GPIO Channels Clock requested */
    OV5640XCLK_GPIO_CLK_ENABLE();

    /* Common configuration for all channels */
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Pull = GPIO_PULLUP;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;

    GPIO_InitStruct.Alternate = OV5640XCLK_GPIO_AF_CHANNEL2;
    GPIO_InitStruct.Pin = OV5640XCLK_GPIO_PIN_CHANNEL2;
    HAL_GPIO_Init(OV5640XCLK_GPIO_PORT_CHANNEL2, &GPIO_InitStruct);
}

void ov5640_dcmi_msp_init(DCMI_HandleTypeDef *hdcmi)
{
    if (hdcmi->Instance == DCMI)
    {
        DMA_HandleTypeDef hdma_handler;
        GPIO_InitTypeDef gpio_init_structure;

        /*** Enable peripherals and GPIO clocks ***/
        /* Enable DCMI clock */
        OV5640_DCMI_CLK_ENABLE();

        /* Enable DMA2 clock */
        OV5640_DMA_CLK_ENABLE();

        /* Enable GPIO clocks */
        OV5640_GPIO_CLK_ENABLE();

        /* Configure DCMI GPIO as alternate function */
        /* DCMI PIXCLK and HSYNC VSYNC pins */
        gpio_init_structure.Pin = OV5640_PIXCLK_GPIO_PIN;
        gpio_init_structure.Mode = GPIO_MODE_AF_PP;
        gpio_init_structure.Pull = GPIO_PULLUP;
        gpio_init_structure.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
        gpio_init_structure.Alternate = GPIO_AF13_DCMI;
        HAL_GPIO_Init(OV5640_PIXCLK_GPIO_PORT, &gpio_init_structure);

        gpio_init_structure.Pin = OV5640_HSYNC_GPIO_PIN;
        HAL_GPIO_Init(OV5640_HSYNC_GPIO_PORT, &gpio_init_structure);

        gpio_init_structure.Pin = OV5640_VSYNC_GPIO_PIN;
        HAL_GPIO_Init(OV5640_VSYNC_GPIO_PORT, &gpio_init_structure);

        /* DCMI D0~ D7 pins */
        gpio_init_structure.Pin = OV5640_DCMI0_GPIO_PIN;
        HAL_GPIO_Init(OV5640_DCMI0_GPIO_PORT, &gpio_init_structure);

        gpio_init_structure.Pin = OV5640_DCMI1_GPIO_PIN;
        HAL_GPIO_Init(OV5640_DCMI1_GPIO_PORT, &gpio_init_structure);

        gpio_init_structure.Pin = OV5640_DCMI2_GPIO_PIN;
        HAL_GPIO_Init(OV5640_DCMI2_GPIO_PORT, &gpio_init_structure);

        gpio_init_structure.Pin = OV5640_DCMI3_GPIO_PIN;
        HAL_GPIO_Init(OV5640_DCMI3_GPIO_PORT, &gpio_init_structure);

        gpio_init_structure.Pin = OV5640_DCMI4_GPIO_PIN;
        HAL_GPIO_Init(OV5640_DCMI4_GPIO_PORT, &gpio_init_structure);

        gpio_init_structure.Pin = OV5640_DCMI5_GPIO_PIN;
        HAL_GPIO_Init(OV5640_DCMI5_GPIO_PORT, &gpio_init_structure);

        gpio_init_structure.Pin = OV5640_DCMI6_GPIO_PIN;
        HAL_GPIO_Init(OV5640_DCMI6_GPIO_PORT, &gpio_init_structure);

        gpio_init_structure.Pin = OV5640_DCMI7_GPIO_PIN;
        HAL_GPIO_Init(OV5640_DCMI7_GPIO_PORT, &gpio_init_structure);

        /*** Configure the DMA ***/
        /* Set the parameters to be configured */
        hdma_handler.Init.Request = DMA_REQUEST_DCMI; // DMA_REQUEST_DCMI_PSSI
        hdma_handler.Init.Direction = DMA_PERIPH_TO_MEMORY;
        hdma_handler.Init.PeriphInc = DMA_PINC_DISABLE;
        hdma_handler.Init.MemInc = DMA_MINC_ENABLE;
        hdma_handler.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;
        hdma_handler.Init.MemDataAlignment = DMA_MDATAALIGN_WORD;
        hdma_handler.Init.Mode = DMA_CIRCULAR;
        hdma_handler.Init.Priority = DMA_PRIORITY_HIGH;
        hdma_handler.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
        hdma_handler.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_FULL;
        hdma_handler.Init.MemBurst = DMA_MBURST_SINGLE;
        hdma_handler.Init.PeriphBurst = DMA_PBURST_SINGLE;

        hdma_handler.Instance = OV5640_DMA_STREAM;

        /* Associate the initialized DMA handle to the DCMI handle */
        __HAL_LINKDMA(hdcmi, DMA_Handle, hdma_handler);

        /*** Configure the NVIC for DCMI and DMA ***/
        /* NVIC configuration for DCMI transfer complete interrupt */
        HAL_NVIC_SetPriority(DCMI_IRQn, OV5640_DCMI_IT_PRIORITY, 0);
        HAL_NVIC_EnableIRQ(DCMI_IRQn);

        /* NVIC configuration for DMA2D transfer complete interrupt */
        HAL_NVIC_SetPriority(OV5640_DMA_IRQn, OV5640_DCMI_IT_PRIORITY, 0);
        HAL_NVIC_EnableIRQ(OV5640_DMA_IRQn);

        /* Configure the DMA stream */
        (void)HAL_DMA_Init(hdcmi->DMA_Handle);
    }
}

void ov5640_dcmi_msp_deinit(DCMI_HandleTypeDef *hdcmi)
{
    GPIO_InitTypeDef gpio_init_structure;

    /* Disable NVIC  for DCMI transfer complete interrupt */
    HAL_NVIC_DisableIRQ(DCMI_IRQn);

    /* Disable NVIC for DMA2 transfer complete interrupt */
    HAL_NVIC_DisableIRQ(OV5640_DMA_IRQn);

    /* Configure the DMA stream */
    (void)HAL_DMA_DeInit(hdcmi->DMA_Handle);

    /* DeInit DCMI GPIOs */
    gpio_init_structure.Pin = OV5640_PIXCLK_GPIO_PIN;
    HAL_GPIO_DeInit(OV5640_PIXCLK_GPIO_PORT, gpio_init_structure.Pin);

    gpio_init_structure.Pin = OV5640_HSYNC_GPIO_PIN;
    HAL_GPIO_DeInit(OV5640_HSYNC_GPIO_PORT, gpio_init_structure.Pin);

    gpio_init_structure.Pin = OV5640_VSYNC_GPIO_PIN;
    HAL_GPIO_DeInit(OV5640_VSYNC_GPIO_PORT, gpio_init_structure.Pin);

    gpio_init_structure.Pin = OV5640_DCMI0_GPIO_PIN;
    HAL_GPIO_DeInit(OV5640_DCMI0_GPIO_PORT, gpio_init_structure.Pin);

    gpio_init_structure.Pin = OV5640_DCMI1_GPIO_PIN;
    HAL_GPIO_DeInit(OV5640_DCMI1_GPIO_PORT, gpio_init_structure.Pin);

    gpio_init_structure.Pin = OV5640_DCMI2_GPIO_PIN;
    HAL_GPIO_DeInit(OV5640_DCMI2_GPIO_PORT, gpio_init_structure.Pin);

    gpio_init_structure.Pin = OV5640_DCMI3_GPIO_PIN;
    HAL_GPIO_DeInit(OV5640_DCMI3_GPIO_PORT, gpio_init_structure.Pin);

    gpio_init_structure.Pin = OV5640_DCMI4_GPIO_PIN;
    HAL_GPIO_DeInit(OV5640_DCMI4_GPIO_PORT, gpio_init_structure.Pin);

    gpio_init_structure.Pin = OV5640_DCMI5_GPIO_PIN;
    HAL_GPIO_DeInit(OV5640_DCMI5_GPIO_PORT, gpio_init_structure.Pin);

    gpio_init_structure.Pin = OV5640_DCMI6_GPIO_PIN;
    HAL_GPIO_DeInit(OV5640_DCMI6_GPIO_PORT, gpio_init_structure.Pin);

    gpio_init_structure.Pin = OV5640_DCMI7_GPIO_PIN;
    HAL_GPIO_DeInit(OV5640_DCMI7_GPIO_PORT, gpio_init_structure.Pin);

    /* Disable DCMI clock */
    __HAL_RCC_DCMI_CLK_DISABLE();
}

void ov5640_i2c_msp_init(I2C_HandleTypeDef *hi2c)
{
    if (hi2c->Instance == OV5640_I2C)
    {
        GPIO_InitTypeDef GPIO_InitStruct;

        /*##-1- Enable peripherals and GPIO Clocks #################################*/
        /* Enable GPIO TX/RX clock */
        OV5640_I2C_SCL_GPIO_CLK_ENABLE();
        OV5640_I2C_SDA_GPIO_CLK_ENABLE();
        /* Enable OV5640_I2C clock */
        OV5640_I2C_CLK_ENABLE();

        /*##-2- Configure peripheral GPIO ##########################################*/
        /* I2C TX GPIO pin configuration  */
        GPIO_InitStruct.Pin = OV5640_I2C_SCL_PIN;
        GPIO_InitStruct.Mode = GPIO_MODE_AF_OD;
        GPIO_InitStruct.Pull = GPIO_PULLUP;
        GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
        GPIO_InitStruct.Alternate = OV5640_I2C_SCL_SDA_AF;
        HAL_GPIO_Init(OV5640_I2C_SCL_GPIO_PORT, &GPIO_InitStruct);

        /* I2C RX GPIO pin configuration  */
        GPIO_InitStruct.Pin = OV5640_I2C_SDA_PIN;
        GPIO_InitStruct.Alternate = OV5640_I2C_SCL_SDA_AF;
        HAL_GPIO_Init(OV5640_I2C_SDA_GPIO_PORT, &GPIO_InitStruct);
    }
}

void ov5640_i2c_msp_deinit(I2C_HandleTypeDef *hi2c)
{
    if (hi2c->Instance == OV5640_I2C)
    {
        GPIO_InitTypeDef gpio_init_structure;

        /* Prevent unused argument(s) compilation warning */
        UNUSED(hi2c);

        /* Configure I2C Tx, Rx as alternate function */
        gpio_init_structure.Pin = OV5640_I2C_SCL_PIN;
        HAL_GPIO_DeInit(OV5640_I2C_SCL_GPIO_PORT, gpio_init_structure.Pin);
        gpio_init_structure.Pin = OV5640_I2C_SDA_PIN;
        HAL_GPIO_DeInit(OV5640_I2C_SDA_GPIO_PORT, gpio_init_structure.Pin);

        /* Disable I2C clock */
        OV5640_I2C_CLK_DISABLE();

        OV5640_I2C_FORCE_RESET();
        OV5640_I2C_RELEASE_RESET();
    }
}

/**
 * @brief  Initializes the DCMI peripheral
 * @param  hdcmi  DCMI handle
 * @note   Being __weak it can be overwritten by the application
 * @retval HAL status
 */
HAL_StatusTypeDef ov5640_dcmi_mx_init(DCMI_HandleTypeDef *hdcmi)
{
    /*** Configures the DCMI to interface with the camera module ***/
    /* DCMI configuration */
    hdcmi->Instance = DCMI;
    hdcmi->Init.CaptureRate = DCMI_CR_ALL_FRAME;
    hdcmi->Init.HSPolarity = DCMI_HSPOLARITY_HIGH;
    hdcmi->Init.SynchroMode = DCMI_SYNCHRO_HARDWARE;
    hdcmi->Init.VSPolarity = DCMI_VSPOLARITY_HIGH;
    hdcmi->Init.ExtendedDataMode = DCMI_EXTEND_DATA_8B;
    hdcmi->Init.PCKPolarity = DCMI_PCKPOLARITY_RISING;

    if (HAL_DCMI_Init(hdcmi) != HAL_OK)
    {
        return HAL_ERROR;
    }
    return HAL_OK;
}

HAL_StatusTypeDef ov5640_i2c_mx_init(I2C_HandleTypeDef *ov5640_i2c_handle)
{
    HAL_StatusTypeDef status = HAL_OK;

    ov5640_i2c_handle->Init.Timing = OV5640_I2C_TIMING;
    ov5640_i2c_handle->Init.OwnAddress1 = 0x01;
    ov5640_i2c_handle->Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT;
    ov5640_i2c_handle->Init.DualAddressMode = I2C_DUALADDRESS_DISABLE;
    ov5640_i2c_handle->Init.OwnAddress2 = 0x00;
    ov5640_i2c_handle->Init.OwnAddress2Masks = I2C_OA2_NOMASK;
    ov5640_i2c_handle->Init.GeneralCallMode = I2C_GENERALCALL_DISABLE;
    ov5640_i2c_handle->Init.NoStretchMode = I2C_NOSTRETCH_DISABLE;

    if (HAL_I2C_Init(ov5640_i2c_handle) != HAL_OK)
    {
        status = HAL_ERROR;
    }
    else
    {
        uint32_t analog_filter;

        analog_filter = I2C_ANALOGFILTER_ENABLE;
        if (HAL_I2CEx_ConfigAnalogFilter(ov5640_i2c_handle, analog_filter) != HAL_OK)
        {
            status = HAL_ERROR;
        }
        else
        {
            if (HAL_I2CEx_ConfigDigitalFilter(ov5640_i2c_handle, OV5640_I2C_DIGITAL_FILTER_COEF) != HAL_OK)
            {
                status = HAL_ERROR;
            }
        }
    }

    /** I2C Enable Fast Mode Plus
     */
    // HAL_I2CEx_EnableFastModePlus(I2C_FASTMODEPLUS_I2C1);
    /* USER CODE BEGIN I2C1_Init 2 */

    return status;
}

static uint8_t ov5640_dcmi_dma_done = 0;

/**
 * @brief  This function handles DCMI interrupt request.
 * @param  Instance Camera instance
 * @retval None
 */
void OV5640_DCMI_IRQHandler(void)
{
    BSP_CAMERA_IRQHandler(0);
}

/**
 * @brief  This function handles DCMI DMA interrupt request.
 * @param  Instance Camera instance
 * @retval None
 */
void OV5640_DMA_IRQHandler(void)
{
    BSP_CAMERA_DMA_IRQHandler(0);
    ov5640_dcmi_dma_done = 1;
}

#include <ov5640_camera.h>
#include <ov5640_error_code.h>
#include <rtthread.h>
#include <bsp_usart.h>
int ov5640_output_test(void)
{
    int rslt = BSP_CAMERA_Init(0, OV5640_R160x120, OV5640_RGB565);
    if (rslt != BSP_ERROR_NONE)
    {
        rt_kprintf("BSP_CAMERA_Init failed, return %d\n", rslt);
        return rslt;
    }

    uint8_t *tx_buf = rt_malloc(160 * 120 * 2);
    rslt = BSP_CAMERA_Start(0, tx_buf, CAMERA_MODE_SNAPSHOT);

    /* if DCMI DMA tx done, call uart1 to transfer tx_buf */
    while (1)
    {
        if (ov5640_dcmi_dma_done)
        {
            // bsp_debug_print_dma(tx_buf, 160 * 120 * 2);
            rt_kprintf("ov5640 capture done \n");
            break;
        }
        rt_thread_mdelay(1000);
    }

    rt_free(tx_buf);

    return rslt;
}
MSH_CMD_EXPORT(ov5640_output_test, "ov5640_output_test");
